home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / AsynchSounds / AsynchSounds.c < prev    next >
Text File  |  1994-11-02  |  7KB  |  295 lines

  1. /*  =====================================================================
  2.     ===========================AsynchSounds.c============================
  3.     =====================================================================
  4.     
  5.     ©1994 by Thomas Reed
  6.     Written using Metrowerks CodeWarrior 4
  7.  
  8.     Please do not distribute modified versions of this code.
  9.  
  10.     If you decide to use this code in a program without making significant
  11.     changes, I don't ask for much -- just mention my name in the About box or
  12.     in a Read Me file (since About box real estate can be valuable!  ;-)
  13.     
  14.     To use, follow these steps:
  15.     
  16.     1)  Change the kSoundErrors and kSoundAlert constants to match the
  17.         appropriate resources in your project.
  18.         
  19.     2)    Call InitChan() to open a new channel.
  20.     
  21.     3)    Call PlaySoundsID(myID) to play a sound of ID myID.
  22.     
  23.     4)    Once you've played with the channel all you want, call
  24.         SendCallBack().  This will place a callback command in the queue
  25.         after all your other commands.  When this command is reached,
  26.         IdleSounds() will notice that sound is done and dispose of the
  27.         channel.
  28.     
  29.     5)    Instead of calling PlaySoundsID() with SendCallBack(), you can
  30.         call PlayResFromDisk(soundID, bufferSize).  This will play the
  31.         sound having ID soundID from disk, loading only bufferSize Kbytes
  32.          at a time.  For large sounds, this uses less memory than
  33.          PlaySoundsID.  The information in step 5 also applies.
  34.     
  35.     6)    Repeatedly call IdleSounds() if you want the channel disposed as
  36.         soon as the sound is finished.  If you want to kill the sounds at
  37.         a particular time, call KillSounds().  Sound will cease instantly,
  38.         and the channel will be disposed of.  BE SURE THAT THE CHANNEL IS
  39.         DISPOSED IN ONE OF THESE WAYS!!!  If the channel doesn't get
  40.         disposed, it spells big trouble the next time the system, or
  41.         another program, tries to make a noise!
  42.     
  43.     7)    To pause the channel, call TellChanWait(myTime).  A waitCmd will be
  44.         added to the queue and the channel will pause when it reaches
  45.         that command.  myTime is the time to pause in half-milliseconds.
  46.         
  47.     8)    If you use this library, all I ask is some credit in an About box or a
  48.         Read Me file somewhere.  If you have any questions, you can get hold of
  49.         me at reed@visar.wustl.edu.
  50.         
  51.     ===================================================================== */
  52.     
  53. #include "AsynchSounds.h"
  54. #include "Sound.h"
  55.  
  56. #define NIL            0L
  57.  
  58. #define kSoundErrors    128
  59. #define kSoundAlert        128
  60.  
  61. #define errCallBack            1
  62. #define errSoundChannel        2
  63. #define errFlush            3
  64. #define errDisposeSound        4
  65. #define errDisposeChannel    5
  66. #define errBuffer            6
  67. #define errMissingSound        7
  68. #define errGetSound            8
  69. #define errQuiet            9
  70. #define errWait                10
  71. #define errDiskPlay            11
  72.  
  73. typedef enum {SR_Unused = 1, SR_Inuse, SR_Finished}     soundRecordState;
  74.  
  75. typedef struct
  76. {
  77.   soundRecordState    state;
  78.   SndChannelPtr        sound_chan;
  79. } mySoundRecord;
  80.  
  81. mySoundRecord    gSounds;
  82. Handle            gSoundToDispose = NIL;
  83.  
  84.  
  85. pascal void ChanCallBack(SndChannelPtr chan, SndCommand cmd);
  86. pascal void Completion(SndChannelPtr chan);
  87.  
  88.  
  89. pascal void ChanCallBack(SndChannelPtr chan, SndCommand cmd)
  90. {
  91.   long    myA5;
  92.   
  93. #ifndef powerc
  94.   myA5 = SetA5(cmd.param2);    /* set A5 to app's A5 */
  95. #endif
  96.   gSounds.state = SR_Finished;
  97. #ifndef powerc
  98.   myA5 = SetA5(myA5);    /* set back to the old A5. */
  99. #endif
  100. }
  101.  
  102. void SendCallBack(void)
  103. {
  104.   SndCommand    myWish;
  105.   OSErr            oe;
  106.   
  107.   myWish.cmd = callBackCmd;
  108.   myWish.param1 = 0;
  109.   myWish.param2 = SetCurrentA5();
  110.   oe = SndDoCommand(gSounds.sound_chan, &myWish, FALSE);
  111.   if (oe != noErr)
  112.     ErrorAlert(errCallBack);
  113. }
  114.  
  115. void InitChan(void)
  116. {
  117.   extern        pascal void ChanCallBack(SndChannelPtr chan, SndCommand cmd);
  118.   OSErr            oe;
  119.   SndCommand    myWish;
  120.  
  121.   gSounds.state = SR_Unused;
  122.   gSounds.sound_chan = NIL;
  123.   oe = SndNewChannel(&gSounds.sound_chan, sampledSynth, initMono, NewSndCallBackProc(ChanCallBack));
  124.   if (oe != noErr)
  125.     ErrorAlert(errSoundChannel);
  126.  
  127.   myWish.cmd = flushCmd;
  128.   myWish.param1 = 0;
  129.   myWish.param2 = 0;
  130.   oe = SndDoImmediate(gSounds.sound_chan, &myWish);
  131.   if (oe != noErr)
  132.     ErrorAlert(errFlush);
  133. }
  134.  
  135. void IdleSounds(void)
  136. {
  137.   if (gSounds.state == SR_Finished)
  138.   {
  139.     gSounds.state = SR_Unused;
  140.     if (gSoundToDispose != NIL)
  141.     {
  142.       HUnlock(gSoundToDispose);
  143.       ReleaseResource(gSoundToDispose);
  144.       if (ResError())
  145.         ErrorAlert(errDisposeSound);
  146.     }
  147.     FinishSounds();
  148.   }
  149. }
  150.  
  151. void FinishSounds(void)
  152. {
  153.   OSErr        oe;
  154.  
  155.   if (gSounds.sound_chan != NIL)
  156.   {
  157.     oe = SndDisposeChannel(gSounds.sound_chan, FALSE);
  158.     if (oe != noErr)
  159.       ErrorAlert(errDisposeChannel);
  160.   }
  161. }
  162.  
  163. void PlaySounds (Handle theSound)
  164. {
  165.   OSErr            oe;
  166.   SndCommand    myWish;
  167.  
  168.   gSoundToDispose = theSound;    /* in case somebody else loaded up */
  169.                                   /* theSound rather than calling */
  170.                                   /* PlaySoundsID. */
  171.  
  172.   if ((theSound != NIL) && (*theSound != NIL))
  173.   {
  174.     IdleSounds();
  175.     MoveHHi(theSound);
  176.     HLock(theSound);
  177.     myWish.cmd = bufferCmd;
  178.     myWish.param1 = 0;
  179.     myWish.param2 = (long) (*theSound + 20);
  180.     oe = SndDoCommand(gSounds.sound_chan, &myWish, FALSE);
  181.     if (oe == noErr)
  182.       gSounds.state = SR_Inuse;
  183.     else
  184.     {
  185.       ErrorAlert(errBuffer);
  186.       gSounds.state = SR_Finished;
  187.     }
  188.   }
  189.   else
  190.     ErrorAlert(errMissingSound);
  191. }
  192.  
  193. void PlaySoundsID (short id)
  194. {
  195.   gSoundToDispose = GetResource('snd ', id);
  196.   if (gSoundToDispose == NIL)
  197.     ErrorAlert(errGetSound); 
  198.   PlaySounds(gSoundToDispose);
  199. }
  200.  
  201. Boolean SoundsDone(void)
  202. {
  203.   if (gSounds.state == SR_Inuse)
  204.     return (FALSE);
  205.   else
  206.     return (TRUE);
  207. }
  208.  
  209. void KillSounds(void)
  210. {
  211.   SndCommand    myWish;
  212.   OSErr            err;
  213.   
  214.   if (SoundsDone())
  215.     return;
  216.   
  217.   myWish.cmd = flushCmd;
  218.   myWish.param1 = 0;
  219.   myWish.param2 = 0;
  220.   err = SndDoImmediate(gSounds.sound_chan, &myWish);
  221.   if (err != noErr)
  222.     ErrorAlert(errFlush);
  223.   
  224.   myWish.cmd = quietCmd;   /* params ignored */
  225.   myWish.param1 = 0;
  226.   myWish.param2 = 0;
  227.   err = SndDoImmediate(gSounds.sound_chan, &myWish);
  228.   if (err != noErr)
  229.     ErrorAlert(errQuiet);
  230.   
  231.   gSounds.state = SR_Unused;
  232.   if (gSoundToDispose != NIL)
  233.   {
  234.     HUnlock(gSoundToDispose);
  235.     ReleaseResource(gSoundToDispose);
  236.     if (ResError())
  237.       ErrorAlert(errDisposeSound);
  238.   }
  239.   FinishSounds();
  240. }
  241.  
  242. void TellChanWait(short halfMilliSecs)
  243. {
  244.   SndCommand    myWish;
  245.   OSErr            oe;
  246.   
  247.   myWish.cmd = waitCmd;
  248.   myWish.param1 = halfMilliSecs;
  249.   myWish.param2 = 0;
  250.   oe = SndDoCommand(gSounds.sound_chan, &myWish, FALSE);
  251.   if (oe != noErr)
  252.     ErrorAlert(errWait);
  253. }
  254.  
  255. pascal void Completion(SndChannelPtr chan)
  256. {
  257.   long    myA5;
  258.   
  259. #ifndef powerc
  260.   myA5 = SetA5(chan->userInfo);    /* set A5 to app's A5 */
  261. #endif
  262.   gSounds.state = SR_Finished;
  263. #ifndef powerc
  264.   myA5 = SetA5(myA5);    /* set back to the old A5. */
  265. #endif
  266. }
  267.  
  268. void PlayResFromDisk(short soundID, short bufSize)
  269. {
  270.   OSErr            oe;
  271.   SndCommand    myWish;
  272.   Handle        theSound;
  273.   
  274.   gSounds.sound_chan->userInfo = SetCurrentA5();
  275.   oe = SndStartFilePlay(gSounds.sound_chan, 0, soundID, bufSize*1024, NIL, NIL, Completion, TRUE);
  276.   if (oe == noErr)
  277.     gSounds.state = SR_Inuse;
  278.   else
  279.   {
  280.     ErrorAlert(errDiskPlay);
  281.     gSounds.state = SR_Finished;
  282.   }
  283. }
  284.  
  285. void ErrorAlert(short errNumber)
  286. {
  287.   short        itemHit;
  288.   Str255    theMessage;
  289.   
  290.   SetCursor(&qd.arrow);
  291.   GetIndString(theMessage, kSoundErrors, errNumber);
  292.   ParamText(theMessage, NIL, NIL, NIL);
  293.   itemHit = StopAlert(kSoundAlert, NIL);
  294. }
  295.